﻿/*
 * @Author: hong
 * @Description: 动态纹理(动态加载3d对象)
 * @Date: 2024年04月18日 星期四 20:04:31
 * @Modify:
 */

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;
#endif

namespace SCore.UI
{
    /// <summary>
    /// 动态纹理
    /// </summary>
    [AddComponentMenu("UI/XUIDynamicTexture")]
    public class UIDynamicTexture : RawImage
    {
        /// <summary>
        /// 纹理宽度
        /// </summary>
        public int width = 256;

        /// <summary>
        /// 纹理高度
        /// </summary>
        public int height = 256;

        /// <summary>
        /// 纹理背景颜色
        /// </summary>
        public Color background = new Color(0, 0, 0, 0);

        /// <summary>
        /// 视域大小
        /// </summary>
        public float size = 1;

        /// <summary>
        /// 渲染内容资源
        /// </summary>
        public GameObject prefab = null;

        /// <summary>
        /// 渲染内容. 兼容旧接口, 叫Content比较合适
        /// </summary>
        public GameObject PrefabClone { get; set; }

        /// <summary>
        /// 渲染单元
        /// </summary>
        public UIDynamicTextureRenderUnit RenderUnit
        {
            get { return _unit; }
            set
            {
                _unit = value;
                if (_unit != null)
                {
                    texture = _unit.RenderTexture;
                    PrefabClone = _unit.Content;
                }
                else
                {
                    texture = null;
                    PrefabClone = null;
                }
            }
        }

        private UIDynamicTextureRenderUnit _unit = null;
        private float _tempAlpha = 0f;

        /// <summary>
        /// 设置渲染内容
        /// </summary>
        /// <param name="prefab_">渲染内容资源</param>
        public void SetPrefab(GameObject prefab_)
        {
            if (prefab_ != null && prefab != null && prefab_.GetInstanceID() == prefab.GetInstanceID())
            {
                return;
            }

            if (Application.isPlaying && _unit != null && isActiveAndEnabled)
            {
                UIDynamicTextureManager.Instance.Release(this);
            }

            prefab = prefab_;

            if (Application.isPlaying && _unit == null && prefab != null && isActiveAndEnabled)
            {
                UIDynamicTextureManager.Instance.Build(this);
            }
        }

        protected override void OnEnable()
        {
            base.OnEnable();
            if (Application.isPlaying && _unit == null && prefab != null)
            {
                UIDynamicTextureManager.Instance.Build(this);
            }
        }

        protected override void OnDisable()
        {
            if (Application.isPlaying && _unit != null)
            {
                UIDynamicTextureManager.Instance.Release(this);
            }

            base.OnDisable();
        }

        private void LateUpdate()
        {
            if (Application.isPlaying)
            {
                // 若渲染内容为空, 则让其透明
                if (prefab == null)
                {
                    if (color.a != 0f)
                    {
                        _tempAlpha = color.a;
                        color = new Color(color.r, color.g, color.b, 0f);
                    }
                }
                else
                {
                    if (_tempAlpha != 0f)
                    {
                        color = new Color(color.r, color.g, color.b, _tempAlpha);
                        _tempAlpha = 0f;
                    }
                }
            }
        }
    }

    /// <summary>
    /// 动态纹理渲染单元
    /// </summary>
    public class UIDynamicTextureRenderUnit : IDisposable
    {
        public const float STANDARD_HEIGHT = 900f;
        public const int RENDER_TEXTURE_DEPTH = 16;

        private const float NEAR_CLIP_PLANE = 0f;
        private const float FAR_CLIP_PLANE = 100f;
        private const float CAMERA_OFFSET = -5f;

        public const float UNIT_SIZE = 100f;
        public const int CHUNK_WIDTHCOUNT = 10;
        public const float CHUNK_OFFSET = -1000f;

        /// <summary>
        /// 获得渲染内容实例
        /// </summary>
        public GameObject Content
        {
            get { return _content; }
        }

        /// <summary>
        /// 获得渲染纹理
        /// </summary>
        public RenderTexture RenderTexture
        {
            get { return _camera.targetTexture; }
        }

        private GameObject _root = null;
        private GameObject _content = null;
        private Camera _camera = null;
        private int _prefabId = 0;
        private int _refCount = 0;

        public int PosIndex { get; private set; }

        /// <summary>
        /// 构建渲染单元
        /// </summary>
        /// <param name="width">纹理宽度</param>
        /// <param name="height">纹理高度</param>
        /// <param name="background">背景颜色</param>
        /// <param name="size">视域大小</param>
        /// <param name="prefab">渲染内容资源</param>
        public UIDynamicTextureRenderUnit(int width, int height, Color background, float size, GameObject prefab,
            int pos)
        {
            float scale = Mathf.Max(Screen.width, Screen.height) / STANDARD_HEIGHT;
            width = (int)(width * scale + 0.5f);
            height = (int)(height * scale + 0.5f);

            _root = new GameObject("Unit");
            _root.transform.SetParent(UIDynamicTextureManager.Instance.Root.transform, false);
            PosIndex = pos;
            RefreshPosition();

            var cameraObject = new GameObject("Camera");
            cameraObject.name = "UIDynamicTextureCamera";
            cameraObject.transform.SetParent(_root.transform, false);
            cameraObject.transform.localPosition = new Vector3(0f, 0f, CAMERA_OFFSET);
            _camera = cameraObject.AddComponent<Camera>();
            _camera.nearClipPlane = NEAR_CLIP_PLANE;
            _camera.farClipPlane = FAR_CLIP_PLANE;
            _camera.targetTexture = RenderTexture.GetTemporary(width, height, RENDER_TEXTURE_DEPTH);
            _camera.clearFlags = CameraClearFlags.Color;
            _camera.backgroundColor = background;
            _camera.orthographic = true;
            _camera.orthographicSize = size;

            _content = UnityEngine.Object.Instantiate(prefab, _root.transform, false);
            _prefabId = prefab.GetInstanceID();
        }

        public void RefreshPosition()
        {
            int x = PosIndex % CHUNK_WIDTHCOUNT;
            int y = PosIndex / CHUNK_WIDTHCOUNT;
            var value = new Vector3(x * UNIT_SIZE + CHUNK_OFFSET, y * UNIT_SIZE + CHUNK_OFFSET);
            _root.transform.position = value;
        }

        /// <summary>
        /// 释放渲染单元
        /// </summary>
        public void Dispose()
        {
            if (_camera != null)
            {
                RenderTexture.ReleaseTemporary(_camera.targetTexture);
            }

            if (_root != null)
            {
                UnityEngine.Object.Destroy(_root);
            }

            _root = null;
            _content = null;
            _camera = null;
            _prefabId = 0;
            _refCount = 0;
        }

        /// <summary>
        /// 是否可被共享
        /// </summary>
        /// <param name="width">纹理宽度</param>
        /// <param name="height">纹理高度</param>
        /// <param name="background">背景颜色</param>
        /// <param name="size">视域大小</param>
        /// <param name="prefab">渲染内容资源</param>
        /// <returns>可被共享返回true, 不可被共享返回false</returns>
        public bool CanShare(int width, int height, Color background, float size, GameObject prefab)
        {
            if (_camera.targetTexture.width == width &&
                _camera.targetTexture.height == height &&
                _camera.backgroundColor == background &&
                _camera.orthographicSize == size &&
                _prefabId == prefab.GetInstanceID())
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 增加引用计数
        /// </summary>
        /// <returns>增加后的引用计数</returns>
        public int IncRef()
        {
            _refCount++;
            return _refCount;
        }

        /// <summary>
        /// 减少引用计数
        /// </summary>
        /// <returns>减少后的引用计数</returns>
        public int DecRef()
        {
            _refCount--;
            return _refCount;
        }
    }

    /// <summary>
    /// 动态纹理管理器
    /// </summary>
    public class UIDynamicTextureManager
    {
        public const int MAXIUMU_RENDER_TEXTURE_SIZE = 512;

        public static readonly UIDynamicTextureManager Instance = new UIDynamicTextureManager();

        public GameObject Root { get; private set; }

        private LinkedList<UIDynamicTextureRenderUnit> _units = new LinkedList<UIDynamicTextureRenderUnit>();
        private LinkedList<int> _EmptyLayoutPosList = new LinkedList<int>();
        private int _peak = 0;

        public void Build(UIDynamicTexture texture)
        {
            if (Root == null) Root = new GameObject("DynamicTexture");

            UIDynamicTextureRenderUnit unit = null;
            foreach (var unit_ in _units)
            {
                if (unit_.CanShare(texture.width, texture.height, texture.background, texture.size, texture.prefab))
                {
                    unit = unit_;
                    break;
                }
            }

            if (unit == null)
            {
                int posIndex = _units.Count;
                if (_EmptyLayoutPosList.Count > 0)
                {
                    posIndex = _EmptyLayoutPosList.First.Value;
                    _EmptyLayoutPosList.RemoveFirst();
                }

                unit = new UIDynamicTextureRenderUnit(texture.width, texture.height, texture.background, texture.size,
                    texture.prefab, posIndex);
                _units.AddLast(unit);
            }

            unit.IncRef();
            //RefreshLayout();
#if UNITY_EDITOR
            PerformanceCheck();
#endif

            texture.RenderUnit = unit;

            if (_peak < _units.Count)
            {
                _peak = _units.Count;
            }
        }

        public void Release(UIDynamicTexture texture)
        {
            var unit = texture.RenderUnit;
            if (unit.DecRef() == 0)
            {
                int recyclePos = unit.PosIndex;

                unit.Dispose();
                _units.Remove(unit);

                if (_units.Count > 0)
                {
                    _EmptyLayoutPosList.AddLast(recyclePos);
                }
                else
                {
                    _EmptyLayoutPosList.Clear();
                }
            }

            texture.RenderUnit = null;
        }

        private void PerformanceCheck()
        {
            var warning = 0;
            foreach (var unit in _units)
            {
                if (unit.RenderTexture.width >= MAXIUMU_RENDER_TEXTURE_SIZE ||
                    unit.RenderTexture.height >= MAXIUMU_RENDER_TEXTURE_SIZE)
                {
                    warning++;
                }
            }

            if (warning > 1)
            {
                foreach (var unit in _units)
                {
                    if (unit.RenderTexture.width >= MAXIUMU_RENDER_TEXTURE_SIZE ||
                        unit.RenderTexture.height >= MAXIUMU_RENDER_TEXTURE_SIZE)
                    {
                        Debug.LogWarningFormat("发现超大XUIDynamicTexture: {0}, {1}x{2}", unit.Content.name,
                            unit.RenderTexture.width, unit.RenderTexture.height);
                    }
                }
            }
        }
    }
}

#if UNITY_EDITOR
namespace SCore.UI
{
    [CustomEditor(typeof(UIDynamicTexture))]
    public class UIDynamicTextureEditor : Editor
    {
        public override void OnInspectorGUI()
        {
            var viewer = target as UIDynamicTexture;
            if (viewer != null)
            {
                viewer.color = EditorGUILayout.ColorField("Color", viewer.color);
                viewer.material =
                    (Material)EditorGUILayout.ObjectField("Material", viewer.material, typeof(Material), false);
                viewer.raycastTarget = EditorGUILayout.Toggle("Raycast Target", viewer.raycastTarget);
                viewer.uvRect = EditorGUILayout.RectField("UV Rect", viewer.uvRect);

                viewer.width = Mathf.Max(1, EditorGUILayout.IntField("Width", viewer.width));
                viewer.height = Mathf.Max(1, EditorGUILayout.IntField("Height", viewer.height));
                viewer.background = EditorGUILayout.ColorField("Background", viewer.background);
                viewer.size = Mathf.Max(0.001f, EditorGUILayout.FloatField("CameraSize", viewer.size));

                var prefab =
                    (GameObject)EditorGUILayout.ObjectField("Prefab", viewer.prefab, typeof(GameObject), false);
                if (prefab != viewer.prefab)
                {
                    if (prefab != null && PrefabUtility.GetPrefabType(prefab) != PrefabType.Prefab)
                    {
                        Debug.LogError("只允许Prefab");
                    }
                    else
                    {
                        viewer.SetPrefab(prefab);
                    }
                }
            }
        }
    }
}
#endif